use crate::frame::context::{Context, UpdateType};
use crate::frame::context::Render;
use crate::frame::App;
use crate::map::Map;
use crate::ui::AppContext;
use crate::window::event::WindowEvent;
#[cfg(feature = "gpu")]
use crate::window::UserEvent;
use crate::window::{WindowId, WindowType};
use crate::*;
#[cfg(feature = "gpu")]
use glyphon::{Cache, Resolution, Viewport};
#[cfg(feature = "gpu")]
use std::error::Error;
use std::sync::Arc;
use std::thread::sleep;
use std::time::Duration;
#[cfg(not(feature = "gpu"))]
use crate::render::image::ImageRender;

pub trait EventLoopHandle {
    fn window_id(&self) -> WindowId;
    fn handle(&self) -> &Arc<WindowType>;
    fn handle_event(&mut self, event: WindowEvent);
}

pub struct LoopWindow {
    pub(crate) app_ctx: AppContext,
    pub(crate) app: Box<dyn App>,
}

impl LoopWindow {
    #[cfg(not(feature = "gpu"))]
    pub fn create_native_window(mut app: Box<dyn App>, wt: Arc<WindowType>, attr: WindowAttribute) -> LoopWindow {
        let context = Context {
            window: wt,
            font: Font::from(&attr.font),
            render: Render { image: ImageRender::new() },
            updates: Map::new(),
            user_update: (WindowId::unique_id(), UpdateType::None),
            new_window: None,
        };
        let device = Device {
            device_input: DeviceInput::new(),
        };
        let mut app_ctx = AppContext::new(device, context, attr);
        app_ctx.draw(&mut app);
        LoopWindow {
            app_ctx,
            app,
        }
    }

    #[cfg(feature = "gpu")]
    pub async fn create_gpu_window(mut app: Box<dyn App>, wt: Arc<WindowType>, attr: WindowAttribute) -> LoopWindow {
        let device = Self::rebuild_device(&wt, attr.inner_size).await.unwrap();
        device.surface.configure(&device.device, &device.surface_config);
        let viewport = Viewport::new(&device.device, &device.cache);
        let context = Context {
            font: Font::from(&attr.font),
            viewport,
            window: wt,
            render: Render::new(&device),
            updates: Map::new(),
            user_update: (WindowId(unique_id_u32()), UpdateType::None),
            new_window: None,
        };
        let mut app_ctx = AppContext::new(device, context, attr);
        app_ctx.draw(&mut app);
        LoopWindow {
            app_ctx,
            app,
        }
    }
    #[cfg(feature = "gpu")]
    pub(crate) async fn rebuild_device(window: &Arc<WindowType>, size: Size) -> Result<Device, Box<dyn Error>> {
        let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
        let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions::default()).await?;
        let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor::default()).await?;
        let cache = Cache::new(&device);
        let surface = instance.create_surface(window.clone()).unwrap();
        let cap = surface.get_capabilities(&adapter);
        let surface_config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format: cap.formats[0],
            view_formats: vec![cap.formats[0].add_srgb_suffix()],
            alpha_mode: wgpu::CompositeAlphaMode::Auto,
            width: size.width_u32(),
            height: size.height_u32(),
            desired_maximum_frame_latency: 2,
            present_mode: wgpu::PresentMode::AutoVsync,
        };
        let w = window.clone();
        device.on_uncaptured_error(Box::new(move |err: wgpu::Error| {
            w.request_update_event(UserEvent::ReInit);
            println!("Error: {:#?}", err);
            println!("{}", err.to_string());
        }));
        Ok(Device {
            device,
            queue,
            cache,
            surface,
            texture_format: cap.formats[0],
            surface_config,
            device_input: DeviceInput::new(),
        })
    }
}

impl EventLoopHandle for LoopWindow {
    fn window_id(&self) -> WindowId {
        self.app_ctx.context.window.id
    }

    fn handle(&self) -> &Arc<WindowType> {
        &self.app_ctx.context.window
    }

    fn handle_event(&mut self, event: WindowEvent) {
        match event {
            WindowEvent::KeyPress(key) => self.app_ctx.update(UpdateType::KeyPress(key), &mut self.app),
            WindowEvent::KeyRelease(key) => self.app_ctx.update(UpdateType::KeyRelease(key), &mut self.app),
            WindowEvent::MouseMove(pos) => {
                self.app_ctx.device.device_input.mouse.update(pos);
                self.app_ctx.update(UpdateType::MouseMove, &mut self.app);
            }
            WindowEvent::MouseWheel(y) => {
                self.app_ctx.device.device_input.mouse.delta = (0.0, y);
                self.app_ctx.update(UpdateType::MouseWheel, &mut self.app);
                self.app_ctx.device.device_input.mouse.delta = (0.0, 0.0);
            }
            WindowEvent::MousePress(pos) => {
                self.app_ctx.device.device_input.mouse.lastest.relative = pos;
                self.app_ctx.device.device_input.mouse.mouse_press();
                self.app_ctx.update(UpdateType::MousePress, &mut self.app);
            }
            WindowEvent::MouseRelease(pos) => {
                self.app_ctx.device.device_input.mouse.lastest.relative = pos;
                self.app_ctx.device.device_input.mouse.mouse_release();
                self.app_ctx.update(UpdateType::MouseRelease, &mut self.app);
                self.app_ctx.device.device_input.mouse.a = 0.0;
            }
            #[cfg(feature = "gpu")]
            WindowEvent::Redraw => {
                self.app_ctx.context.viewport.update(&self.app_ctx.device.queue, Resolution {
                    width: self.app_ctx.device.surface_config.width,
                    height: self.app_ctx.device.surface_config.height,
                });
                self.app_ctx.redraw(&mut self.app, None); //, None, None
            }
            #[cfg(not(feature = "gpu"))]
            WindowEvent::Redraw(paint) => {
                self.app_ctx.redraw(&mut self.app, Some(paint))
            }

            WindowEvent::ReInit => {
                //休眠15秒，保证系统底层、设备恢复
                sleep(Duration::from_secs(15));
                self.app_ctx.update(UpdateType::ReInit, &mut self.app)
            }
            #[cfg(feature = "gpu")]
            WindowEvent::Resize(size) => {
                self.app_ctx.device.surface_config.width = size.width_u32();
                self.app_ctx.device.surface_config.height = size.height_u32();
                let device = &self.app_ctx.device.device;
                let config = &self.app_ctx.device.surface_config;
                self.app_ctx.device.surface.configure(device, config);
            }
            WindowEvent::ReqUpdate => self.app_ctx.update(self.app_ctx.context.user_update.1.clone(), &mut self.app),
            WindowEvent::IME(data) => self.app_ctx.update(UpdateType::IME(data), &mut self.app),
            WindowEvent::Clipboard(data) => self.app_ctx.update(UpdateType::Clipboard(data), &mut self.app),
            WindowEvent::UserUpdate => self.app_ctx.user_update(&mut self.app),
            #[cfg(not(feature = "gpu"))]
            _ => {}
        }
    }
}
